﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MT4Trainer.Parallel_Helpers
{
    using System.Collections.Concurrent;
    using System.Threading;
    using System.Threading.Tasks;

    internal class Parallel_Sort<T>
    {
        public static void ParallelQuickSort(
            T[] data, IComparer<T> comparer, int maxDepth = 16, int minBlockSize = 10000)
        {
            // call the internal method
            doSort(data, 0, data.Length - 1, comparer, 0, maxDepth, minBlockSize);
        }

        internal static void doSort(
            T[] data, int startIndex, int endIndex, IComparer<T> comparer, int depth, int maxDepth, int minBlockSize)
        {
            if (startIndex < endIndex)
            {
                // if we have exceeded the depth threshold or there are
                // fewer items than we would like, then use sequential sort
                if (depth > maxDepth || endIndex - startIndex < minBlockSize)
                {
                    Array.Sort(data, startIndex, endIndex - startIndex + 1, comparer);
                }
                else
                {
                    // we need to parallelize
                    int pivotIndex = partitionBlock(data, startIndex, endIndex, comparer);
                    // recurse on the left and right blocks
                    Task leftTask = Task.Factory.StartNew(
                        () =>
                        { doSort(data, startIndex, pivotIndex - 1, comparer, depth + 1, maxDepth, minBlockSize); });
                    Task rightTask = Task.Factory.StartNew(
                        () =>
                        { doSort(data, pivotIndex + 1, endIndex, comparer, depth + 1, maxDepth, minBlockSize); });


                    // wait for the tasks to complete
                    Task.WaitAll(leftTask, rightTask);
                }
            }
        }

        internal static void swapValues(T[] data, int firstIndex, int secondIndex)
        {
            T holder = data[firstIndex];
            data[firstIndex] = data[secondIndex];
            data[secondIndex] = holder;
        }

        internal static int partitionBlock(T[] data, int startIndex, int endIndex, IComparer<T> comparer)
        {
            // get the pivot value - we will be comparing all
            // of the other items against this value
            T pivot = data[startIndex];
            // put the pivot value at the end of block
            swapValues(data, startIndex, endIndex);
            // index used to store values smaller than the pivot
            int storeIndex = startIndex;
            // iterate through the items in the block
            for (int i = startIndex; i < endIndex; i++)
            {
                // look for items that are smaller or equal to the pivot
                if (comparer.Compare(data[i], pivot) <= 0)
                {
                    // move the value and increment the index
                    swapValues(data, i, storeIndex);
                    storeIndex++;
                }
            }


            swapValues(data, storeIndex, endIndex);
            return storeIndex;
        }


        public static string WriteHelp()
        {
            // perform the parallel sort
            //Console.WriteLine(
            //    "Heres an int example :  Parallel_Sort<int>.ParallelQuickSort(sourceData, new IntComparer())");
            return "Heres an int example :  Parallel_Sort<int>.ParallelQuickSort(sourceData, new IntComparer())";
        }

        public class IntComparer : IComparer<int>
        {
            public int Compare(int first, int second)
            {
                return first.CompareTo(second);
            }
        }
    }

    public class Tree<T>
    {
        public Tree<T> LeftNode, RightNode;

        public T Data;
    }

    internal class TreeTraverser
    {
        public static void TraverseTree<T>(Tree<T> tree, Action<T> action)
        {
            if (tree != null)
            {
                // invoke the action for the data
                action.Invoke(tree.Data);
                // start tasks to process the left and right nodes if they exist
                if (tree.LeftNode != null && tree.RightNode != null)
                {
                    Task leftTask = Task.Factory.StartNew(() => TraverseTree(tree.LeftNode, action));
                    Task rightTask = Task.Factory.StartNew(() => TraverseTree(tree.RightNode, action));
                    // wait for the tasks to complete
                    Task.WaitAll(leftTask, rightTask);
                }
            }

        }
    }

    internal class TreeSearch
    {
        public static T SearchTree<T>(Tree<T> tree, Func<T, bool> searchFunction)
        {
            // create the cancellation token source
            CancellationTokenSource tokenSource = new CancellationTokenSource();
            // search the tree
            TWrapper<T> result = performSearch(tree, searchFunction, tokenSource);
            return result == null ? default(T) : result.Value;
        }

        private class TWrapper<T>
        {
            public T Value;
        }

        private static TWrapper<T> performSearch<T>(
            Tree<T> tree, Func<T, bool> searchFunction, CancellationTokenSource tokenSource)
        {
            // define the result
            TWrapper<T> result = null;
            // only proceed if we have something to search
            if (tree != null)
            {
                // apply the search function to the current tree
                if (searchFunction(tree.Data))
                {
                    //cancel the token source
                    tokenSource.Cancel();
                    // set the result
                    result = new TWrapper<T>() { Value = tree.Data };
                }
                else
                {
                    // we have not found a result - continue the search
                    if (tree.LeftNode != null && tree.RightNode != null)
                    {
                        // start the task for the left node
                        Task<TWrapper<T>> leftTask =
                            Task<TWrapper<T>>.Factory.StartNew(
                                () => performSearch(tree.LeftNode, searchFunction, tokenSource), tokenSource.Token);
                        // start the task for the right node
                        Task<TWrapper<T>> rightTask =
                            Task<TWrapper<T>>.Factory.StartNew(
                                () => performSearch(tree.RightNode, searchFunction, tokenSource), tokenSource.Token);
                        try
                        {
                            // set the result based on the tasks
                            result = leftTask.Result != null
                                         ? leftTask.Result
                                         : rightTask.Result != null ? rightTask.Result : null;
                        }
                        catch (AggregateException)
                        {
                        }
                    }
                }
            }
            // return the result
            return result;
        }


        private static Tree<int> populateTree(Tree<int> parentNode, Random rnd, int depth = 0)
        {
            parentNode.Data = rnd.Next(1, 1000);
            if (depth < 10)
            {
                parentNode.LeftNode = new Tree<int>();
                parentNode.RightNode = new Tree<int>();
                populateTree(parentNode.LeftNode, rnd, depth + 1);
                populateTree(parentNode.RightNode, rnd, depth + 1);
            }
            return parentNode;
        }
    }

    internal class Parallel_Reduce
    {
        public static TValue Reduce<TValue>(
            TValue[] sourceData, TValue seedValue, Func<TValue, TValue, TValue> reduceFunction)
        {
            // perform the reduction
            return sourceData.AsParallel().Aggregate(
                seedValue,
                (localResult, value) => reduceFunction(localResult, value),
                (overallResult, localResult) => reduceFunction(overallResult, localResult),
                overallResult => overallResult);
        }
    }

    internal class Decoupled_Console
    {
        // queue-based blocking collection
        private static BlockingCollection<Action> blockingQueue;

        // task that processes messages to the console
        private static Task messageWorker;

        static Decoupled_Console()
        {
            // create the blocking collection
            blockingQueue = new BlockingCollection<Action>();
            // create and start the worker task
            messageWorker = Task.Factory.StartNew(
                () =>
                {
                    foreach (Action action in blockingQueue.GetConsumingEnumerable())
                    {
                        // invoke the action
                        action.Invoke();
                    }
                },
                TaskCreationOptions.LongRunning);
        }

        public static void WriteLine(object value)
        {
            blockingQueue.Add(new Action(() => Console.WriteLine(value)));
        }

        public static void WriteLine(string format, params object[] values)
        {
            blockingQueue.Add(new Action(() => Console.WriteLine(format, values)));
        }
    }
    
}